package com.ms.service.module.fish.test;

import java.nio.ByteBuffer;
import java.util.Arrays;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;

import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.net.XSEPacketCategoryMeter;
import com.xpec.c4.service.message.NetMessage;
import com.xpec.c4.service.utility.JavaZip;

public class XSENetNettyDecoder extends FrameDecoder 
{
	private static final int MIN_PACKET_SIZE = 6;
	
	public static final int MAX_DATA_LENGTH = (1 << 16) - 1;;
	
	public static final byte START_FLAG1 = (byte) (Short.MAX_VALUE >> 8);
	public static final byte START_FLAG2 = (byte) (Short.MAX_VALUE);
	
	static final MyThreadLocalByteArray decoderTempMem = new MyThreadLocalByteArray();
	
	////////////////////////////////////////////////////////////////////
	
	public XSENetNettyDecoder()
	{
	}

	
	@Override
	protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer)
	{
		if( buffer.readableBytes() < MIN_PACKET_SIZE )
			return null;		
		
		/// 先訂一個標記, 之後才能將指標快速回到這個標記的地方
		buffer.markReaderIndex();
		
		/// 我們的消息結構
		while( buffer.readableBytes() >= MIN_PACKET_SIZE )
		{
			byte magic1 = buffer.readByte();
			if( magic1 != START_FLAG1 )
			{
				buffer.markReaderIndex();
				channel.close();
				buffer.readBytes(buffer.readableBytes());
				return null;
				//continue;
			}
			
			byte magic2 = buffer.readByte();
			if( magic2 != START_FLAG2 )
			{
				buffer.markReaderIndex();
				channel.close();
				buffer.readBytes(buffer.readableBytes());
				return null;
				//continue;
			}
			
			int dataLength = buffer.readInt();
			if( buffer.readableBytes() < dataLength )
			{
				buffer.resetReaderIndex();
				return null;
			}
			
			/// 獲取每個 thread 唯一的一份 mem buffer
			// TODO 默认的buff size是65535，如果超了，就会报错
			ByteBuffer memBuffer = decoderTempMem.getClearBuffer(dataLength);
			memBuffer.limit( dataLength ); /// 設定只需要 copy 這麼多
			buffer.readBytes( memBuffer );
			memBuffer.flip();
			
			// 讀完封包資料後將 read index 移到最後面 
//			buffer.readerIndex(buffer.writerIndex());
			
			int messageType = memBuffer.getInt();
			if (messageType != MessageTypes.S_FISH_ACTION_DISPATCH) {
				return null;
			}

			/// 先判斷是否要解壓縮
			byte isCompress = memBuffer.get();
			if( isCompress==1 )
			{
				byte[] memByteArray = decoderTempMem.getClearByteArray();
				
				memBuffer.get(memByteArray, 0, memBuffer.remaining());
				byte[] newBytes = JavaZip.decompressBytes( memByteArray );
				
				memBuffer.clear();
				memBuffer.put(newBytes);
				memBuffer.flip();
			}
			
			NetMessage msg = new NetMessage();
			msg.setMessageType( messageType );
			
			try 
			{
				byte[] tmp = new byte[memBuffer.remaining()];
				memBuffer.get(tmp);
				
				msg.setMessageContentBytes(tmp);

			} catch (Exception e) {
				e.printStackTrace();
			}
			
			XSEPacketCategoryMeter.getInstance().recordInput(messageType, msg.getMessageContentBytes().length);
			return msg;
		}
		
		buffer.resetReaderIndex();
		return null;
	}

	////////////////////////////////////////////////////////////////////
	/// 預先替每個 thread 都準備一個 byte[], 免得一直在 allocate 這個 byte[]
	
	static class MyByteBuffer
	{
		public byte[] byteArray = null;
		public ByteBuffer byteBuffer = null;
	}
	static class MyThreadLocalByteArray extends ThreadLocal<MyByteBuffer>
	{
		private int size = XSENetNettyDecoder.MAX_DATA_LENGTH;
		
		public MyThreadLocalByteArray(){}
		public MyThreadLocalByteArray(int bufferSize){
			size = bufferSize;
		}
		
		@Override
		protected synchronized MyByteBuffer initialValue(){
			MyByteBuffer buffer = new MyByteBuffer();
			buffer.byteArray = new byte[size];
			buffer.byteBuffer = ByteBuffer.allocate( size );
			return buffer;
		}
		
		public ByteBuffer getClearBuffer(){
			MyByteBuffer buffer = super.get();
			buffer.byteBuffer.clear();
			return buffer.byteBuffer;
		}

		public ByteBuffer getClearBuffer(int size){
			MyByteBuffer buffer = super.get();
			ByteBuffer bb = buffer.byteBuffer;
			if(bb.capacity() < size){
				return ByteBuffer.allocate(size);
			}
			bb.clear();
			return bb;
		}
		
		public byte[] getClearByteArray(){
			MyByteBuffer buffer = super.get();
			Arrays.fill(buffer.byteArray, (byte)0);
			return buffer.byteArray;
		}
	}
}